.TITLE DCROP - Read operations .IDENT /05.00/ ; ; Copyright (c) 1995-1999 by Mentec, Inc., U.S.A. ; All rights reserved. ; ; Written by: John Gemignani ; ; Re-mastered for RSX-11M-PLUS version 4.4 by: ; ; L. B. McCulley ; D. Carroll ; ; Modified for RSX-11M-PLUS V4.5 by: ; ; D. Carroll 15-Apr-1993 4.04 ; DC191 - Correct signed branch in check for request ; being too big, i.e. 128 block read ; ; Modified for RSX-11M-PLUS V4.6 by: ; ; D. Carroll 08-Jan-1996 05.00 ; DC430 - Modified for handle 32-bit LBNs ; ; ; For RSX-11M-PLUS V4.4 a major change to the organization and ; functionality of disk data caching was implimented. By adopting a queued ; implimentation with write tracking, a number of issues are addressed, ; including; ; ; o Increase throughput by handling multiple extents to process a ; request. ; o Insure that read-ahead cannot read beyond file locks and aquire ; stale data which is in the process of being written. ; o Insure that no stale deferred extents exist in cache, and the ; appropriate read-through is implimented. ; ; ; This module contains the code which preforms cache read operations. An I/O ; proceeds through the following steps: ; ; 1) Phase I - determine if the request should be processed by cache ; the cacher will process the request if ; caching is enabled for the operation type (eg, IO.LOV ; AND ; request length is less than limit specified by user ; If cache does not process request, ensure no conflicting ; data exists in cache, and return C-set. ; ; 1.1) Allocate a Cache Request Packet (CRP). Holds operation context ; so duplicate I/O packets are not required. ; ; Initialize CRP with context from $DCCEB et el set up by ; $MPVBN for read-ahead. ; ; 1.2) Queue User Request Packet (URP, from DRQRQ call to cacher) ; to driver and let cache phase 2 handle completion ; of all cache ops. ; ; Cache Phase II is queue-driven with all cache operation packets being ; queued FIFO to serialize all processing. Each packet is process ; as much as possible, if it blocks on a resource (extent) busy (from ; other request, or read in progress to load for this op) it is queued ; to the CED. The operation complete that unbusies the extent will ; re-queue all waiting operations to the Phase II dispatcher for ; further processing. All queueing is FIFO for serialization. ; ; 2.1) Any existing extents involved? CALL SEARCH to find out. ; ; 2.1a) If one is found, does it cover beginning of request? ; ; - "cache miss" if none found, or request start not covered ; - "cache hit" if extent found that covers beginning of request ; ; MISS - Determine if this miss is the only "missing extent" for the ; request. If not, purge out other extents, to insure only one ; I/O request is issued. ; Then, handle by loading required extent with read-ahead attempt ; Allocate an extent, and attach to the newly allocated CED. ; Issue I/O to driver using DRP, update CRP parameters to ; reflect request requirements after new extent is loaded ; and transferred. Internal ioc returns the driver error ; status to the user and deletes the extent for errors. For ; successful loads, ioc passes control to transfer process ; common with "cache hit" processing which handles all further ; processing (eg, transfer, continuation of overlaps, etc. ; When all processing for this extent has been completed, ; IOC will pass control to the Phase II dispatcher to process ; any request waiting for the extent that just completed load ; ; HIT - Attach to the extent. If extent is busy, request will be ; queued to extent and requeued to Phase II for retry when ; extent goes unbusy. If extent is not busy, perform transfer ; and detach from extent. Detach will requeue any pending ; requests for the extent now that we are done with it, for ; Phase II processing (this is for IOC code path). Remaining ; request size is checked and if request is done status is ; returned to user else next cache extent is processed. ; ;- .IF DF D$$CHE .PSECT DC$ROP, RO, I .SBTTL Read Phase I ;- ; **-DCR1- Read Phase I ; ; Passed: ; R1 -> I/O packet ; I.EFN := Sign bit indicates virtual operation ; I.TCB -> TCB of issuing task ; T.ST3 := T3.ACP indicates task is an ACP ; R5 -> UCB ; UCBX mapped in APR6 ; X.CSTS := Unit's cache status (i.e. selected operations) ; $CXDBL non-zero = context switching disabled (eg, user I/O from ; ; Returned: ; C-bit set if cache cannot process operation, otherwise C-bit clear ; ; Action: ; Determine if the function is to be processed by cache. If so, ; set up a Cache Request Packet in the cache internal pool to hold ; context across possible driver calls or cache queueing intervals ; ; Queue the request to cache Phase II dispatcher for serialization ; with other cache requests. This includes a call to $FORK to allow ; other possible IOC processing. ; ; NOTE revisions are being made in RSX-11M-PLUS v4.4 to the handling of ; overlapped extents. The requirement for strict serialization of all ; cache operation processing (because of race conditions between writes ; and read-aheads, occuring below the level of executive disk block lock ; means that a queued processing architecture must be adopted. This lends ; itself to the handling of requests having multiple partially overlapped ; cache extents (the most difficult case) by simply sequentially stepping ; through the segments of the request as either hits or misses, and handling ; the simple degenerate cases in series until the operation completes. ; ; It turns out that this eliminates considerable complexity in the cache ; code required to handle exception cases (eg, deferred extents forced ; to be written due to partial overlap but unable to complete immediatel ; because of disk queue latency or primary pool depletion). As a result ; the implementation of full overlapped extent support is believed more ; robust than the previous attempt to dodge the issue. ; ; The special exception is the case where a single I/O request would require ; multiple individual I/O's to be sent to disk. This causes excessive overhead ; as well as compromises shadowing integrity, unless the shadowing case is ; handled as a seperate case. ; ; Notes: ; ACP requests which are issued in order to handle the file system ; (F11) are issued with context switching enabled (i.e. $CXDBL is 0) ; Requests on behalf of a task for disk extent crossings (i.e. map ; failures) are issued with context switching disabled (i.e. $CXDB ; non-zero). ; ; ; Until the first MP.PAR (or MP$PAR) function is issued, the resident ; cache code is mapped in Kernel-D APR5, which allows local data to ; be used as a dispatch table. This is not possible after phase II ; is invoked, since the cache region will be mapped in Kernel-D space ; after completing the call to FORK. ; ;- .SBTTL + Local data for dispatch operations ; IOLOV: .WORD XC.OVR, APR6.BASE+X.XOVR, APR6.BASE+S.OVR .SBTTL + DCOV1 - Load overlay ;+ ; DCOV1 - entry point from DCDSP for IO.LOV, IO.LDO overlay loads ;- DCOV1:: MOV #IOLOV,R0 ; point to data for overlays BR COMMON ; rest is common with re TOOBIG: USTAT$ S.RBIG,R0 ; tally a read that was too big QUICK: CALLR CHEPUR ; purge deferred extents .SBTTL + DCR1 - Read operations ;+ ; DCR1 - entry point from DCDSP for read operations phase I ; ; Determine the type of operation, and re-join common code ;- DCR1:: CALL INICTX ; initialize context BCC COMMON ; join common code, not virtual I/O BITB #XC.RDA,APR6.BASE+X.CSTS ; Read-ahead / preload enabled BNE COMMON ; yes, go ahead CLR $DCCEL ; no, show no valid prelod info ASSUME COMMON,. ; fall through to common .SBTTL + Common dispatching code for read functions ;+ ; Common processing code for all read operations in phase I ; ; Input: ; ; R0 -> Pointer to status table ; R1 -> URP address ; R5 -> UCB address ; U.UCBX -> Secondary pool address of UCBX ; X.CSTS := Unit's cache status, selected operation ; X.XDAT := Virtual extent limit ; X.XRDA := Read ahead extent limit ; X.XOVR := Overlay extent limit ; X.XLOG := Logical extent limit ; X.XDIR := Directory extent limit ; ; Local data is mapped ; ; (Additional parameters set for virtual reads, ; courtesy of $MPVBN) ; ; $DCSTS - Window status from file window ; $DCCEB - MSP of base LBN for extent ; $DCCEB+2 - LSP of base LBN for extent ; $DCCEL - Length of extent from base LBN ; ; Structure returned by INICTX; ; ; 0(R0) - I/O status flag XC.xxx ; 2(R0) - Address of extent limit variable ; 4(R0) - Base of block used for statistics ; ;- COMMON: BITB (R0)+,APR6.BASE+X.CSTS ; is this option enabled for cache BEQ QUICK ; nope, finish up ... INC R0 ; normalize index MOVB @(R0)+,-(SP) ; save the maximum extent size MOV (R0),R0 ; and get the statistic address ;+ ; After this point, no data in the cache code may be referenced, ; since on an I/D space system, the cache partition will be mapped ; in both Kernel APR 5 and 6. ; ; USTAT$ insures KISAR5, and KISAR6 are mapped if needed ; ;- USTAT$ S.RTOT,R0 ; Increment read total CALL CLCLEN ; calc blocks to read - return in R4 CMPB R4,(SP)+ ; compare request size with size limit BHI TOOBIG ; and bypass cache if too big MP.PAR ; map cache partition for GETCRP fallback CALL GETCRP ; save params in CRP, tries to delete from age BCS QUICK ; age list if depletion but just in case MOV R0,R.STAT(R2) ; set stats context in CRP ; ; Operation will be processed by cache ; ; We need to save the read-ahead information about the file extent ; (from $MPVBN) because we may need to use it in Phase 2 after we're ; queued for long enough to have another user QIO run through the system ; We use the CRP as the context storage block, and we can use I.PRM+P6 ; here because if that were meaningful the bypass bits would have skipped ; this code. ; ; ; (Additional parameters set for virtual reads, ; courtesy of $MPVBN) ; ; $DCSTS - Window status from file window ; $DCCEB - MSP of base LBN for extent ; $DCCEB+2 - LSP of base LBN for extent ; $DCCEL - Length of extent from base LBN ; 15$: TSTB I.EFN(R1) ; was this a virtual function? BPL 20$ ; if PL, no, logical of some form ; If MI, yes, maybe can do readahead TST $DCCEL ; do we have valid read-ahead data BNE 25$ ; virt ops should always have it 20$: MOV I.PRM+P5(R1),R.LBNL(R2) ; save I/O parameters MOV I.PRM+P5(R1),R.LBNH(R2) ; ...as read-ahead value MOV I.PRM+P4(R1),R.LBNL+2(R2) ; copy the P4 parameter ;DC430 BIT #DV.32B,U.CW1(R5) ; 32-bit LBNs? ;DC430 BNE 22$ ; if NE, yes, continue ;DC430 CLRB R.LBNL+3(R2) ; use only 24-bits ;DC430 ;DC430 22$: MOV R.LBNL+2(R2),R.LBNH+2(R2) ;DC430 ADD R4,R.LBNH(R2) ; update ending block ;DC430 ADC R.LBNH+2(R2) ; double precision ;DC430 BR 30$ ; go to work on it! ;**-4 25$: MOV $DCCEB,R.LBNL+2(R2) ; save file extent info from $MPVBN ;DC430 MOV $DCCEB+2,R.LBNL(R2) ; in CRP for cache read-ahead ;DC430 MOV $DCCEB,R.LBNH+2(R2) ; save file extent info as high ;DC430 MOV $DCCEB+2,R.LBNH(R2) ; low bounds for preloads ;**-3 ADD $DCCEL,R.LBNH(R2) ; as highest LBN in file extent ADC R.LBNH+2(R2) ; MSP of double-precision LBN 30$: CLR $DCCEL ; show read-ahead data stale CALLR Q2PHS1 ; synchronize thru phase II .SBTTL + DCR2 - Data Cache Read phase 2 processing entry ; ; DCR2:: -- Phase 2 entry for Data Cache Read operation processing ; ; ; At this point, any return will not go back to $DRQRQ, but back ; to Phase II processing. ; ; Input: (from phase II dispatch) ; ; R1 -> URP, with CRP allocated ; R5 -> UCB ; UCBX mapped ; ; SEARCH returns: ; Cache partition mapped in APR6 ; ; R0 -> CED of first overlapping extent ; R2 := Highest LBN requested Least significant part (LSP) ; R3 := " " " Most " " (MSP) ; R4 := Number of LBNs requested ; ; Note: ; Don't forget that the extent highest LBN is stored +1. ; ; Existing context is preserved and used as much as possible. ; DCR2:: MOV I.ACED(R1),R0 ; retrieve ptr to attached CED if any extent BEQ 10$ ; must do SEARCH if not already attached ;+ ; If we have an attached extent, then we have just completed an I/O ; to load the request, and now we should go transfer the data ; into the user buffer, and continue the scan. ;- MP.PAR ; map cache partition if SEARCH skipped CALL UPDPKT ; just completed so refresh request params,(in BR XFR ; case preload!) then go do xfr for this ;+ ; No attached extent, so scan to determine if there are any overlapping ; extents in cache. ;- 10$: CALL SEARCH ; Find the first overlapping extent BCS MISS ; no extent overlaps range at all .SBTTL + TSTOLP, Test for extent overlap on hit ;+ ; Test for possible overlap after start of request ; ; Input: ; R0 -> Next CED which overlaps request ; R1 -> URP ; R2 -> Highest LBN requested (LSP) ; R3 -> Highest LBN requested (MSP) ; R4 -> Number of blocks requested ; R5 -> UCB ; Cache partition mapped ;- TSTOLP: MOV I.PRM+P4(R1),-(SP) ; save the high order LBN ;DC430 BIT #DV.32B,U.CW1(R5) ; supporting 32-bit devices ;DC430 BNE 10$ ; if NE, yes, continue ;DC430 CLRB 1(SP) ; clean up the high order part ;DC430 10$: CMP (SP)+, E.LBNL+2(R0) ; Rqst start before exte ;DC430 BHI HIT ; If HI, no, hit on it ;**-1 BLO MISS ; If LO, yes CMP I.PRM+P5(R1), E.LBNL(R0) ; Double word comparison BLO MISS ; If LO yes, else hit .SBTTL + HIT, Process cache hit ;+ ; Found extent in cache for next request segment, process it ; ; Input: ; R0 -> Next CED which overlaps request ; R1 -> URP ; R2 -> Highest LBN requested (LSP) ; R3 -> Highest LBN requested (MSP) ; R4 -> Number of blocks requested ; R5 -> UCB ; Cache partition mapped ;- HIT: BITB #ES.ERR,E.STAT(R0) ; Check for deferred extent w/error BEQ 10$ ; no error, process normally BITB #ES.WDF,E.STAT(R0) ; data exist on disk? BEQ RDERR ; yes, driver can try again w/o ; else return error code here ;+ ; We have a hit, on a deferred extent which encountered an error ; when attempting to write the extent to disk ... Since we will ; now report the error, we do not have to maintain this extent ; in memory until the file is deleted, so clean up now ... ;- BICB #,E.STAT(R0) ; we will report the error, so clean CALL FRECED ; and delete the CED MOV #,R0 ; Indicate error (bad block error) BR RDFIN ;+ ; Attach to this CED, make sure it's not already busy ;- 10$: CALL ATTCED ; Attach to CED, queue if it's busy BCS DONE ; CED was busy, return later ;+ ; Tally a read hit, but only one read hit on any given I/O ; request, even if multiple CEDs are required to satisfy ; the request. ;- MOV I.CRP(R1),R2 ; get the CRP pointer BIT #1,R.STAT(R2) ; have we already logged a read hit? BNE XFR ; if NE, yes, don't log multiple hits USTAT$ S.RHIT ; Now we've got it, increment read hit BIS #1,R.STAT(R2) ; flag that we have tallied a read .SBTTL + XFR, Transfer CED contents to User buffer ;+ ; **-XFR-- Transfer this CED to the user buffer ; ; Input: ; R0 -> CED which is part of the URP ; R1 -> URP ; R5 -> UCB ; Cache partition mapped ; XFR: BISB #ES.XIP,E.STAT(R0) ; show extent busy w/xfr in prog CALL RDXFR ; move data to user buffer, update URP BICB #ES.XIP,E.STAT(R0) ; show extent no longer busy CALL DETCED ; release ownership lock on CED TST I.PRM+P2(R1) ; more bytes requested? BEQ RDDON ; nope, all done, return success ;+ ; Find next overlapping CED in LBN list (if any) ; based on R0 being the next CED in the list to process. ;- CALL SRCHP ; partial search, start pt set BCS MISS ; no overlapping CED, is a miss BR TSTOLP ; see if next CED covers next bl .SBTTL + Read completion routines ;+ ; Request is now complete. P3 is the total of the bytes transferred ; for this request. It must be placed in R1 (R0 will indicate success) ; and the packet is forwarded to $IOFIN. ;- RDDON: MOV #,R0 ;Indicate success RDFIN: CALL RLSCRP ; refresh URP (w/stat), release MOV R1,R3 ; Setup $IOFIN context MOV I.PRM+P3(R3),R1 ;Set the count of bytes moved MOV I.UCB(R3),R5 ;R5 -> Disk UCB for completion MOV R5,-(SP) ; save UCB address around $IOFIN CALL $IOFIN ;Complete the I/O MOV (SP)+,R5 ; and restore UCB address DONE: RETURN ; to dispatch next Phase II process ;+ ; RDERR - error on read of data from disk ; handle by passing request to driver, let driver return error code ;- RDERR: CALL RLSCRP ; release CRP, refreshing the original URP CALLR .DRQRQ ; pass request to driver .SBTTL + MISS - load next segment of request if missing ;+ ; MISS: load missing portion of request (eg, the portion before any ; extent found in SEARCH, or whole request if no existing extent overlap ; then let ioc run Phase II to finish processing. ; ; NOTE: handles read-ahead (actually, preloads as much of disk file extent ; as possible in addition to requested blocks) ; ; Passed: ; R0 -> next CED (or maybe zero) ; R1 -> I/O request packet ; I.CRP -> CRP with saved user I/O request params ; I.ACED -> used as flags cell ; APRD.BASE+H.TMP -> prev CED in LBN list (from SEARCH) ; ; Action: ; If cache is in rundown abort caching of request, insure all involved ; extents are deleted prior to forwarding on to the driver. ; Calculate parameters for actual read with pre-loading. ; Allocate extent, set up packet and queue to driver ; RDIOC will continue request processing via Phase II ; ; ; We need to try read-ahead first, to get the actual length (and incidentally ; the starting LBN) for the transfer. The length is required for the call ; to GETBUF, which returns the CED for the transfer. GETBUF parameters ; R1 => pkt, R4 = length, R5 => UCB. GETBUF returns R0 => CED if allocated ; successful (FREAGE is called from GETBUF until no more space is freed, ; allocation failures should be rare). If there is an allocation failure ; attach to a CED which has outstanding I/O, or queue the packet for cache ; phase 2 processing and return to caller in hopes ; that something will free up by the time phase 2 runs again. ; ; Context: ; R0 -> CED (or 0) for next overlapping extent ; R1 -> I/O packet ; R2 -> Highest LBN requested (LSP) ; R3 -> Highest LBN requested (MSP) ; R4 -> Number of blocks requested ; R5 -> UCB ; Cache partition mapped ; ; This algorithm has evolved, and the current flow is as follows; ; ; a) If deactivation is active, goto (f) ; b) Determine if there are any extents overlapping which are ; deferred. If so, goto (f) ; c) Only one I/O required to satisfy this request? If not, read ; under cache, to satisfy the read ; d) Pre-age all overlapping extents, and load new extent ; ; f) Delete all overlapping extents, and then load new extent ;- MISS: MOV KISAR6,-(SP) ; save KISAR6 MP.UCBX ; map the UCB Extention MOVB APR6.BASE+X.CST2,(R1) ; get the deactivation status MOV (SP)+,KISAR6 ; restore partition mapping BITB #X2.DEA,(R1) ; are we in deactivation BNE 20$ ; yup, flush, and read-through TST R0 ; any overlap at all? BEQ 35$ ; nothing to test CALL RDSIZ ; determine our read size BCS 15$ ; if CS, multiple I/O requests MOV R0,I.ACED(R1) ; save CED address 10$: CALL QUPAGE ; rejuvinate this CED CMP R0,(R1) ; is this the last extent BEQ 35$ ; yup, go and load info MOV E.LNXT(R0),R0 ; get the next CED BNE 10$ ; if NE, more to go ... BR 35$ 15$: BITB #,(R1) ; any DFR/WPL extents involved BEQ 25$ ; if EQ, nope, read under 20$: TST R0 ; any extents involved? BEQ 30$ ; nope, just queue to driver CALL DELATT ; delete all involved extents BCC 25$ ; and read-under cache USTAT$ S.RLAP ; flag overlap sent to driver CLR (R1) ; reset statistics address RETURN ; wait for phase II again 25$: USTAT$ S.RLAP ; flag overlap sent to driver 30$: CLR I.ACED(R1) ; insure no CED shown CALL UPDPKT ; and update the user params BR 70$ ; and forward to the disk 35$: MOV I.ACED(R1),R0 ; reset previous "next" CLR I.ACED(R1) ; and reset the flag ;+ ; First try read-ahead (always pre-load as much as possible) ; and then aquire a cache buffer for the target extent ;- CALL PRELOD ; calculate read limits for max preload CALL GETBUF ; Get a cache buffer BCC 50$ ; got a buffer, go ahead ;+ ; Here we failed to get a cache buffer. It is possible that either there ; is a lack of pool for the CED, or the buffer. If we are required to purge ; the world, don't purge to load the largest extent, but instead purge to ; get the requested size ... ; ; If this also fails, then attempt to queue back to through phase II, or ; attach to the CED which is queued to be written out to disk. ;- CALL UPDPKT ; restore rqst params (PRELOD mungs em) CALL PRELD2 ; recalculate our size w/o read-ahead CALL GETBUF ; try to get a buffer w/ new parameters BCC 50$ ; if CC, we got one this time CALL RFRSHP ; restore original parameters TST R0 ; did we get a CED to attach to? BEQ 40$ ; nope, just requeue for later CALLR ATTCED ; attach to this CED 40$: CALLR Q2PHS1 ; return queued to Phase 2 ;+ ; Prepare the driver packet. Cannot depend on context from user QIO$. ; This sets the parameters that are associated with this specific transfer ; The user request parameters that were stored in the Cache Request Packet ; for later restoration have been used for the pre-load computations. ; ; Context: ; R0 -> CED of extent being loaded ; R1 -> I/O packet ; R4 := size of request in 512-byte blocks ; ; Complete the setup of the packet now that we know this I/O will go ;- 50$: CALL ATTCED ; Better succeed, it's a new CED ; Setup the new CED LBN/size parameters MOVB #ES.RIP, E.STAT(R0) ; Flag as read-in-progress CALL SETLBN ; set up LBN/size parameters ; Put CED into age list and LBN list CALL QADAGE ; Insert the CED into the age list CALL QINLBN ; and into the unit LBN list ; Set extent buffer address in packet MOV R1,R3 ; save packet address MOV E.PHYA(R0), R1 ; Buffer address doubleword MOV #140000, R2 ; In APR6 CALL $MPPHY ; Map to physical address if NPR MOV R1, I.PRM+P1.1(R3) ; Set the most significant part MOV R2, I.PRM+P1.2(R3) ; Set the least " " MOV R3, R1 ; Restore packet address ; Update read load statistics USTAT$ S.RLOD ; Now we're sure, increment read ; Store length to read in request packet ASH #11,R4 ; R4 is length as 512-byte block MOV R4, I.PRM+P2(R1) ; make it bytes for QIO pkt ;+ ; Common entry for both partial cache hit, and cache load functions ; ; Set fixed fields in I/O packet ; ; Function: IO.RLB ; Int IOC : RDIOC: ;- 70$: MOV #IO.RLB,I.FCN(R1) ; set to IO.RLB w/no modifiers MOV KINAR5, I.IOSB+2(R1) ; Setup internal I/O completion MOV #,I.IOSB+4(R1) ; Address must be in APR5!, note bit 0 CLRB I.EFN(R1) ; insure as logical I/O ;+ ;**-1 ; Note: ; Everything must be ready for IOC when we call $DRQRQ, the driver ; may race us back here. So we need to have set up all the extent ; values that need to be initialized from the I/O packet, before any ; of the drivers get their grubby paws on it. ; ; Read I/O Complete will refresh the User Request Packet from ; the Cache Request Packet and then pass it to phase II. ;- CALLR .DRQRQ ; Send to driver .SBTTL + PRELOD - calculate limits for read-ahead (preload) ; ; PRELOD - routine to calculate high and low LBN limits for read ; in order to preload maximum number of blocks to cache ; uses file extent bounds saved in CRP at cache read entry ; ; Parameters: ; R0 -> CED or zero ; R1 -> packet for request ; APRD.BASE+H.TMP -> prev CED in LBN list (from SEARCH) ; ; Returned: ; R1 -> packet, starting LBN (P4 & P5) updated ; R4 = number of blocks to be read ; ; R0, R2 trashed ; ; ; first set up by retrieving max read ahead ;- PRELOD: CLR -(SP) ; check read-ahead size limit MOV KISAR6,-(SP) ; save KISAR6 MP.UCBX BISB APR6.BASE+X.XRDA,2(SP) ; get max extent size with read-ahead MOV (SP)+,KISAR6 ; restore KISAR6 mapping BR PRECMN ; join common code PRELD2: CLR -(SP) ; set max read-ahead as extent size MOV APRD.BASE+H.TMP,R0 ; get previous CED in LBN list BNE 10$ ; if NE, we can get the "next" MOV KISAR6,-(SP) ; save kernel APR6 MP.UCBX ; map the UCBX MOV APR6.BASE+X.CCED,R0 ; get the first CED in the list MOV (SP)+,KISAR6 ; restore mapping BR PRECMN ; and continue ... 10$: MOV E.LNXT(R0),R0 ; get the next CED in LBN list PRECMN: MOV I.CRP(R1),R2 ; retreive CRP address ;+ ; Since all I/O operations appear to have read-ahead, we will check to ; insure that the request length is in range so that this algorithm ; will not cause a smaller amount to be read simply based on the fact ; that read-ahead is smaller than the request size. If we are here ; then the request is in range! ;- CALL CLCLEN ; calculate blocks to transfer CMP R4,(SP) ; is the length > read-ahead BLOS 5$ ; if LOS, nope, continue MOV R4,(SP) ; use request length as max 5$: ; reference label ;+ ; ; Find the LBN bounding the start of the read, as higher of: ; end of preceding cache extent, or start of disk extent. ; ;- MOV R.LBNL+2(R2),R3 ; assume disk extent bounds read ;DC430 MOV R.LBNL(R2),R4 ; ..dbl prec.. ;**-2 MOV R0,-(SP) ; save address of next CED in LBN MOV APRD.BASE+H.TMP,R0 ; get R0 -> prev CED in LBN list BEQ 20$ ; none, disk extent is the bound ; is top of previous CED above disk extent start? CMP R3,E.LBNH+2(R0) ; disk extent start before prev ;DC430 BHI 20$ ; no, ok now, get real length to read ;**-1 BLO 10$ ; yes, use top of prev CED as bounds CMP R4,E.LBNH(R0) ; ..dbl prec.. BHIS 20$ ; start is ok, get length to read 10$: MOV E.LBNH(R0),R4 ; 20$: MOV (SP)+,R0 ; restore ptr to next CED in LBN MOV R4,-(SP) ; save low order LBN of lower bounds ;+ ; ; Find the LBN bounding end of read, as lower of: ; start of following cache extent, or end of disk extent. ; ;- MOV R.LBNH+2(R2),R3 ; assume disk extent higher ;DC430 MOV R.LBNH(R2),R4 ; ...LBN is upper bound ;**-2 TST R0 ; R0 -> next CED in LBN list BEQ 50$ ; last extent in cache, no problem ; read ahead to end of file extent CMP R3,E.LBNL+2(R0) ; compare file extent max LBN ;DC430 BLO 50$ ; ...with start of next extent ;**-1 BHI 30$ ; use LBN from CED to bound read CMP R4,E.LBNL(R0) ; LSP of double precision BLOS 50$ ; read ahead to end of file extent 30$: ; read to start of overlapping CED MOV E.LBNL+2(R0),R3 ; ..dbl prec.. ;DC430 MOV E.LBNL(R0),R4 ; next CED LBN bounds top of read ;**-2 ; R3,R4 = LBN bounding end of possible read 50$: MOV R4,R0 ; trash R0 to save R4 ; (carry/borrow ok here!) SUB (SP)+,R0 ; compute length of possible read CMP R0,(SP) ; is this greater than max allow BHIS 55$ ; yes, use max length allowed MOV R0,(SP) ; no, use possible length 55$: SUB (SP),R4 ; find start of possible read SBCB R3 ; handle carry here! MOV I.PRM+P4(R1),-(SP) ; assume 32-bit LBNs ;DC430 BIT #DV.32B,U.CW1(R5) ; correct assumption ;DC430 BNE 56$ ; if NE, yes, use 32-bits ;DC430 CLRB 1(SP) ; clean up high order part ;DC430 ;DC430 56$: CMP R3,(SP)+ ; does this read start before request ;DC430 BLO 60$ ; yes, use preload start ;**-1 BHI 70$ ; no, use start of request as is CMP R4,I.PRM+P5(R1) ; ..dbl prec.. BHIS 70$ ; rqst ok as is 60$: MOV R4,I.PRM+P5(R1) ; ..dbl prec.. ;DC430 MOVB R3,I.PRM+P4(R1) ; use computed preload start ;DC430 BIT #DV.32B,U.CW1(R5) ; allow 32-bit LBNs ;DC430 BEQ 70$ ; nope, just bytes ;DC430 MOV R3,I.PRM+P4(R1) ; load in the full word ;DC430 70$: ;**-2 MOV (SP)+,R4 ; get length to read 99$: RETURN .SBTTL + RDSIZ - Determine number of I/Os needed ;+ ; **-RDSIZ-- Determine if deferred extents or multiple I/O is needed ; ; This routine will size the current request, and determine if ; there is any chance of requiring multiple I/O requests to satisfy ; a request, or if there are any deferred extents which would cause ; a read under cache to cause a problem. ; ; Input: ; R0 - First CED involved in overlap ; R1 - URP ; R2 - LSP of highest LBN ; R3 - MSP of highest LBN ; R4 - Size of request ; R5 - UCB address of device ; ; Cache partition mapped ; ; ; Output: ; CC-C clear -- Only one I/O is required ; (R1) last CED involved ; CC-C set -- Multiple I/O is required ; (R1) logical OR of all E.STAT bits ;- RDSIZ: MOV R4,-(SP) ; save R4 for a bit MOV R0,-(SP) ; and the starting CED CLR (R1) ; init our flags area CMP R3,E.LBNL+2(R0) ; this extent start above ;DC430 BLO 30$ ; yup, skip it ... ;**-1 BHI 10$ ; if HI, this is part CMP R2,E.LBNL(R0) ; and now the MSP BLO 30$ ; this is the last one . 10$: BISB E.STAT(R0),(R1) ; include current status CMP R3,E.LBNH+2(R0) ; will this CED complete ;DC430 BLO 30$ ; if LO, yes, it will ;**-1 BHI 20$ ; if HI, nope, it will not CMP R2,E.LBNH(R0) ; how about the low order BLO 30$ ; if LO, yes it will 20$: MOV E.LNXT(R0),R4 ; get the next CED BEQ 40$ ; if EQ, multiple I/O required CMP E.LBNL+2(R4),E.LBNH+2(R0) ; are these contiguous? ;DC430 BNE 40$ ; if NE, no, check defer ;**-1 CMP E.LBNL(R4),E.LBNH(R0) ; how about low order? BNE 40$ ; nope, check deferred MOV R4,R0 ; reset the next CED BR 10$ ; and check this one 30$: BIT #,(R1) ; any extents leaving? BNE 40$ ; yup, flag multiple I/O MOV R0,(R1) ; save the last CED TST (PC)+ ; skip the SEC instruction 40$: SEC ; indicate overlap 50$: MOV (SP)+,R0 ; restore first CED address MOV (SP)+,R4 ; and the size value RETURN ; to caller .SBTTL + RDIOC - Internal I/O completion for read loads ;+ ; **-RDIOC- Internal I/O Completion for read loads ; ; Passed: ; R3 -> driver I/O request packet that is completing ; I.PRM+P3(R3) -> driver status ; R5 -> UCB address ; ; Returns: ; CED unbusied. ; Queued requests for CED queued to Phase II. ; Phase II invoked (if needed) ; ; Actions: ; On error, return status to user and delete CED. ;- RDIOC: CALL $SAVNR ; save registers - for $IOFIN caller MOV R3,R1 ; set up our internal I/O packet ptr MOV I.UCB(R3),R5 ; get UCB (and UCBX) for device MP.PAR ; maps cache partition for this device MOV I.ACED(R3),R0 ; get CED for this request ;**-1 BEQ 30$ ; we have a read completion w/o CED BITB #CS.DBG,APRD.BASE+H.CSTS ; debug enabled? BEQ 7$ ; if EQ, nope, skip this CMP E.IOPA(R0),R3 ; is this the packet for this ced? BNE 3$ ; no, go die ... CMP E.UCB(R0),R5 ; ucb match ced? BEQ 7$ ; ok, sanity checked 3$: FATAL$ BE.UCB ; bugcheck w/ cache UCB error 7$: BICB #ES.RIP,E.STAT(R0) ; clear read in progress flag TSTB I.PRM+P3(R3) ; Get the error status from driver BPL 10$ ; no error, complete and continue BISB #,E.STAT(R0) ; set error flags for extent CALL DETCED ; detach CED also deletes if ES.DEL set ; and requeues waiting rqsts CALL RLSCRP ; refresh URP, delete CRP CALL .DRQRQ ; give to driver, let it return status BR 20$ ; and go run cache phase II if necessary 10$: ; RDOKAY -- Read succeeded, now finish processing in Phase 2 MOV R3,R1 ; set conventional register to CALL Q2PHS2 ; queue packet to Phase 2 now 20$: CALLR DCFRK0 ; to Phase II dispatcher fork 30$: MOV I.CRP(R1),R2 ; get the CRP address ADD I.PRM+P4(R1),R.P3(R2) ; update total bytes transferred MOV I.PRM+P3(R1),R0 ; load the final status CALLR RDFIN ; and terminate the read .SBTTL + RDXFR - Transfer data ; ; Passed: ; Cache partition mapped in APR6 ; ; R0 -> CED to transfer ; R1 -> I/O Request packet ; I.PRM+P1 -> user buffer address (dbl prec. or addrs dbl-word) ; I.PRM+P2 -> byte count requested ; I.PRM+P3 -> reserved for cache scratchpad ; I.PRM+P4 -> MSP of first requested LBN ; I.PRM+P5 -> LSP of first requested LBN ; I.PRM+P6.1 -> CED ; I.CRP -> CRP for this I/O request packet ; ; Returned: ;;; ; I/O request packet updated for unsatisfied portion after transfer ; I.PRM+P1 add bytes transfered (double precision) ; I.PRM+P2 subtract bytes transferred ; I.PRM+P4, P5 add blocks transferred (double precision) ; ; ; Note: ; Disk devices enforce word alignment, and since cache extents ; are also word aligned, data transfers are fast using ; the executive's $BLXIO routine. (A single 256 word ; disk block takes approximately 600us on a PDP-11/70.) ; ; Parameter 3 of the user's I/O packet is used as the sum ; of bytes moved. ; ; ; The algorithm is as follows: ;; ; 1. Initialize the count of bytes and blocks moved. ; (do first to use results to update request values) ; Determine the amount of data available in the ; initial extent by subtracting the high LBN from ; the user's request LBN. This gives the number of ; blocks to move ("blocks"). In bytes, this is ; multipled by 512 ("current"). If "current" is ; larger than the request (I.PRM+P2), then P2 ; will replace "current". ; ; 2a. The data's starting APR address is the user's ; block number less the LBNL (starting LBN). ; This number multiplied by 8 is used as the APR ; bias of the starting address (since all buffers ; in cache are APR aligned, the virtual address is ; only 120000 or 140000). ; 2b. Update the next requested block number using the ; count previously calculated in step 1. ; ; 3a. The user buffer address is taken from the request ; packet. This may need to be adjusted, since NPR devices ; will have real 22-bit addresses in I.PRM+0 and I.PRM+2. ; $BLXIO expects to see an address doubleword. "ADJBUF" in ; DCSUB is used to do this adjustment, and the contents of ; I.PRM+0,I.PRM+2 are saved and restored for use in the next ; request. ; 3b. Update the request packet to give the next user buffer ; address following the data transferred in this call. ; ; 4. Load the byte count for BLXIO into R0, it was calculated ; and saved earlier because R0 was needed. ; 4b. Subtract the amount moved "moved" from the requested ; byte count (I.PRM+P2). ; ; 5. Setup and call $BLXIO to transfer the data. ; ; ; Calculate lengths to actually transfer - bytes and blocks. ; ; Notes: ; We can ignore excess blocks in update of next requested LBN if ; user request ends before end of extent since there is no next xfr ; ; We ignore the high order bits here, since this CED is ; definitely connected with this request, and we are ; allowed to borrow from the most significant bits. ; Our result is a single precision count of blocks ; which are transferrable from the CED, and a count of ; bytes to actually transfer. ; ;- RDXFR: MOV R1,-(SP) ; save i/o packet ptr MOV I.CRP(R1),R2 ; reference CRP thru R2, URP thru R1 ; update CRP to reflect transfer ; use URP for transient operation ; Here we get the lesser of user rqst length or extent length MOV E.LBNH(R0),R3 ;Get LSP of extent high LBN+1 SUB I.PRM+P5(R1),R3 ;Subtract LSP of user request ; gives number blocks transferred ASH #9., R3 ;Convert to a byte bount CMP I.PRM+P2(R1),R3 ;User ask to transfer less? BHIS 20$ ;No if HIS MOV I.PRM+P2(R1),R3 ;Else replace extent by user buffer 20$: MOV R3,-(SP) ;Save EBLXIO byte count param ;+ ; Now set source parameters for EBLXIO call ; calculated from request and extent information ; (done out of order because of register usage sequencing) ; ; R4 = destination APR6 displacement ; R3 = destination APR6 bias ; NOTE: must be adjusted for possible ; address doubleword format variant (eg, if NPR device) ;- MOV R.P1.1(R2),I.PRM+P1.1(R1) ; set up user buffer address MOV R.P1.2(R2),I.PRM+P1.2(R1) ; for 'ADJBUF' call ADD R.P3(R2),I.PRM+P1.2(R1) ; offset for previous xfr ADCB I.PRM+P1.1+1(R1) ; just in case... ADD (SP),R.P3(R2) ;now update total bytes moved for IO CALL ADJBUF ;Adjust user's buffer address MOV I.PRM+P1.1(R1),R3 ;Get adjusted destination address MOV I.PRM+P1.2(R1),R4 ;...both parts ; R1 = Source APR5 bias MOV I.PRM+P5(R1),R2 ;Get rqst base LBN, subtract extent SUB E.LBNL(R0),R2 ; base for block offset into extent ASH #3, R2 ;Make it an APR bias offset ADD E.PHYA(R0),R2 ;Now R2 is APR bias of cache buffer MOV R2,R1 ;Make R1 param for EBLXIO ; R0 = byte count to transfer MOV (SP)+,R0 ;Get saved length to transfer ; R2 = Source APR5 displacment MOV #APR5.BASE,R2 ; Extent is block-aligned, APR5 CALL EBLXIO ; Transfer the data. ;+ ; Request is now complete. P3 in CRP is the running total of the bytes ; transferred for this request. It must be maintained for return to $IOFIN ; URP has been used as a scratchpad for this extent I/O, must be refreshed ; from remaining user request params in CRP. ;- MOV (SP)+,R1 ; restore I/O pkt ptr to R1 ; update request parameters for bytes transferred in all prev sequences CALL UPDPKT ; refresh packet, update for transfer MOV I.ACED(R1),R0 ; get CED ptr for next search ; now get remaining blocks needed to satisfy request CALL CLCLEN ; calculate the length required ;+ ; at return we want and have (in URP): ; P1 updated by adding bytes xfr'd ; P2 updated by subtracting bytes xfr'd ; P3 d/c ; P4/P5 updated by adding blocks transferred ; P6 d/c ; CRP: ; P3 updated to be running total of bytes transferred ;- MOV I.UCB(R1),R5 ; restore UCB ptr RETURN .ENDC ; .IF DF D$$CHE .END